package com.ceair.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ceair.entity.SysAuthority;
import com.ceair.entity.request.authority.AuthorityInOrUpReq;
import com.ceair.entity.request.authority.DeleteAuthorityReq;
import com.ceair.entity.vo.authority.SysAuthorityVO;
import com.ceair.entity.vo.role.RoleAuthorityVO;
import com.ceair.enums.DeleteStatus;
import com.ceair.enums.ResultCode;
import com.ceair.exception.BusinessException;
import com.ceair.mapper.SysAuthorityMapper;
import com.ceair.service.ISysAuthorityService;
import com.ceair.utils.structMapper.SysAuthorityStructMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单表 服务实现类
 * </p>
 *
 * @author wangbaohai
 * @since 2025-03-03
 */
@Service
@Slf4j
public class SysAuthorityServiceImpl extends ServiceImpl<SysAuthorityMapper, SysAuthority> implements ISysAuthorityService {

    /**
     * 查询所有权限信息
     * <p>
     * 此方法用于获取系统中所有未被删除的权限信息，并将其转换为SysAuthorityVO对象列表返回
     * 主要执行以下操作：
     * 1. 查询所有未删除的菜单
     * 2. 使用 SysAuthorityStructMapper 转换输出结果
     *
     * @return 包含所有未删除权限信息的SysAuthorityVO对象列表
     */
    @Override
    public List<RoleAuthorityVO> queryAllAuthority() {
        // 查询所有未删除的菜单
        List<SysAuthority> sysAuthorities = lambdaQuery().eq(SysAuthority::getDeleted, DeleteStatus.NOT_DELETED).list();

        // 使用 SysAuthorityStructMapper 转换输出结果
        List<SysAuthorityVO> sysAuthorityVOS =
                sysAuthorities.stream().map(SysAuthorityStructMapper.INSTANCE::toVO).toList();

        // 整理树型结构并输出
        return buildRoleAuthorityTree(sysAuthorityVOS);
    }

    /**
     * 保存或更新权限信息
     * <p>
     * 此方法用于保存或更新系统权限信息。根据传入的 `AuthorityInOrUpReq` 对象，将非空字段复制到 `SysAuthority` 实体对象中，
     * 然后调用 `saveOrUpdate` 方法进行保存或更新操作。最后，将保存或更新后的 `SysAuthority` 对象转换为 `SysAuthorityVO` 对象并返回。
     *
     * @param authorityInOrUpReq 包含权限信息的请求对象，用于创建或更新权限
     * @return 返回保存或更新后的权限信息视图对象（SysAuthorityVO）
     */
    @Override
    public SysAuthorityVO save(AuthorityInOrUpReq authorityInOrUpReq) {
        // 初始化 SysAuthority 对象
        SysAuthority sysAuthority = new SysAuthority();

        // 将 authorityInOrUpReq 中的不是空的数据赋值给 sysAuthority，避免空值覆盖已有数据
        BeanUtil.copyProperties(authorityInOrUpReq, sysAuthority, CopyOptions.create().ignoreNullValue());

        // 直接使用 saveOrUpdate 方法，如果存在则更新，不存在则插入
        saveOrUpdate(sysAuthority);

        // 将 SysAuthority 对象转换为 SysAuthorityVO 对象并返回
        return SysAuthorityStructMapper.INSTANCE.toVO(sysAuthority);
    }

    /**
     * 删除权限信息
     *
     * 此方法用于逻辑删除系统权限信息它首先检查要删除的权限ID列表是否为空或null，
     * 然后验证这些权限下是否还有子菜单或按钮存在如果存在，则抛出异常否则，
     * 它将标记这些权限为已删除状态
     *
     * @param deleteAuthorityReq 包含要删除的权限ID列表的请求对象
     * @return 返回删除操作是否成功
     * @throws BusinessException 如果输入的权限ID列表为空、null或存在下级菜单/按钮时抛出业务异常
     */
    @Override
    @Transactional
    public Boolean delete(DeleteAuthorityReq deleteAuthorityReq) {
        // 明确要删除的权限id清单
        List<Long> ids = deleteAuthorityReq.getIds();

        // 判空
        if (ids == null || ids.isEmpty()) {
            log.warn("传入的权限ID列表为空或null，删除操作失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "传入的权限ID列表为空或null，删除操作失败");
        }

        // 检查是否有下级菜单或者按钮，有的话异常
        List<SysAuthority> sysAuthorities = lambdaQuery()
                .in(SysAuthority::getMenuPid, ids)
                .eq(SysAuthority::getDeleted, DeleteStatus.NOT_DELETED)
                .list();

        // 异常保留
        if (!sysAuthorities.isEmpty()) {
            log.warn("传入的权限ID列表中存在有下级菜单或者按钮，删除操作失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "传入的权限ID列表中存在有下级菜单或者按钮，删除操作失败");
        }

        // 构建批量逻辑删除角色对象
        List<SysAuthority> sysAuthorityList = ids.stream().map(id -> {
            SysAuthority sysAuthority = new SysAuthority();
            sysAuthority.setId(id);
            sysAuthority.setDeleted(DeleteStatus.DELETED.getCode());
            return sysAuthority;
        }).toList();

        // 执行批量逻辑删除
        return updateBatchById(sysAuthorityList);
    }


    /**
     * 将 List<SysAuthorityVO> 中所有菜单数据，复制到 RoleAuthorityVO 中，并根据 menuPid 建立父子关系
     *
     * @param sysList 所有系统菜单数据（平铺结构）
     * @return 树形结构的角色权限数据列表
     */
    private List<RoleAuthorityVO> buildRoleAuthorityTree(List<SysAuthorityVO> sysList) {
        // 首先判空
        if (sysList == null || sysList.isEmpty()) {
            return Collections.emptyList();
        }

        // 第一步：复制 SysAuthorityVO 到 RoleAuthorityVO，复制相同属性
        List<RoleAuthorityVO> roleList = sysList.stream().filter(Objects::nonNull).map(sys -> {
            RoleAuthorityVO role = new RoleAuthorityVO();
            role.setId(sys.getId());
            role.setName(sys.getName());
            role.setMenuPid(sys.getMenuPid());
            role.setComponent(sys.getComponent());
            role.setAuthority(sys.getAuthority());
            role.setBtnSign(sys.getBtnSign());
            role.setHidden(sys.getHidden());
            role.setIcon(sys.getIcon());
            role.setSort(sys.getSort());
            role.setType(sys.getType());
            role.setDeleted(sys.getDeleted());
            role.setChildren(new ArrayList<>()); // 提前初始化 children 列表
            return role;
        }).toList();

        // 第二步：构造 Map，以 id 为键，便于快速查找父节点
        Map<Long, RoleAuthorityVO> map = roleList.stream()
                .collect(Collectors.toMap(RoleAuthorityVO::getId, role -> role));

        // 第三步：遍历 roleList，根据 menuPid 组装树形结构
        List<RoleAuthorityVO> tree = new ArrayList<>();
        for (RoleAuthorityVO role : roleList) {
            Long menuPid = role.getMenuPid();
            if (menuPid == null || menuPid == 0) {
                tree.add(role);
            } else {
                RoleAuthorityVO parent = map.get(menuPid);
                if (parent != null) {
                    parent.getChildren().add(role);
                } else {
                    // 记录日志或采取其他措施，确保不会丢失数据
                    log.info("Parent node not found for menuPid: {}", menuPid);
                }
            }
        }

        /**
         * 第四步：对 tree 进行排序，根据 sort 属性，并且需要递归对子节点进行排序
         */
        sortTree(tree);
        return tree;
    }

    /**
     * 对角色权限树进行排序
     *
     * @param tree 角色权限树，表示具有层级结构的角色权限信息列表
     *
     * 此方法的目的是对给定的角色权限树进行递归排序，首先对当前层级的节点进行排序，
     * 然后递归地对每个节点的子节点进行排序，以确保整个树结构按照预定义的顺序排列
     *
     * 使用递归的原因是角色权限树具有层级结构，每一层的节点可能还包含下一层级的节点，
     * 通过递归可以确保对树中的所有节点进行排序处理
     */
    public void sortTree(List<RoleAuthorityVO> tree) {
        if (tree == null || tree.isEmpty()) {
            return; // 防止空指针异常
        }

        // 对当前层级进行排序
        tree.sort(Comparator.comparingInt(RoleAuthorityVO::getSort));

        // 递归对子节点进行排序
        for (RoleAuthorityVO node : tree) {
            List<RoleAuthorityVO> children = node.getChildren(); // 子节点存储在 children 字段中
            if (children != null && !children.isEmpty()) {
                sortTree(children); // 递归调用
            }
        }
    }

}
